﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using gwBedrockApp;

namespace gwBedrockApp
{
    /// <summary>
    /// Create Application Instance
    /// </summary>
    partial class gwApplicationCreator
    {
        static gwApplicationCreator()
        {
            gwApplication.Instance = new GatewayServer();
        }
    }

    //gate way server
    public class GatewayServer : gwBedrockApp.gwApplication, IMessageOperator
    {
        #region global instance
        /// <summary>
        /// global instance
        /// </summary>
        public static GatewayServer GetInstance
        {
            get
            {
                return (GatewayServer)Instance;
            }
        }
        #endregion

        #region private variable
        //buffer size
        int m_bufferLength;
        //message cache object
        MessageCache m_messageCache;
        //lock object
        Object m_lockObj = new Object();
        #endregion

        #region constructor
        public GatewayServer()
        {
            this.AppName = "GatewayServer";
            this.AppVersion = "1.0.0.0";
            this.CompanyName = "GrayWind";

            m_bufferLength = 0;
        }
        #endregion

        #region virtual call
        private MessageCacheUnit tempUnit;
        /// <summary>
        /// override open() function
        /// </summary>
        /// <returns></returns>
        protected override bool Open()
        {
            base.Open();

            do
            {
                {
                    System.Console.WriteLine("Application Name:{0}", AppName);
                    System.Console.WriteLine("Application Verion:{0}", AppVersion);
                    System.Console.WriteLine("Company Name:{0}", CompanyName);

                    System.Console.WriteLine("Options:");
                    System.Console.WriteLine("         -p(Port. default 3375)");
                    System.Console.WriteLine("         -a(AuthServer IP. default 127.0.0.1)");
                    System.Console.WriteLine("         -ap(AuthServer Port. default 3376)");
                    System.Console.WriteLine("         -s(MaxSession. default 50)");
                    System.Console.WriteLine("         -t(I/O ThreadNum. default 1)");
                    System.Console.WriteLine("         -b(Buffer Length(kb). defalt 64)");

                    NetLib.create_service();
                    System.Console.WriteLine("Create Network Service Completely!");
                    int nPort = CommandLineArgs.GetInt("p", 3375);
                    int nMaxSession = CommandLineArgs.GetInt("s", 50);
                    int nTheads = CommandLineArgs.GetInt("t", 1);
                    m_bufferLength = CommandLineArgs.GetInt("b", 64);
                    m_bufferLength *= 1024;

                    m_authIP = CommandLineArgs.GetString("a", "127.0.0.1");
                    m_authPort = CommandLineArgs.GetInt("ap", 3376);

                    System.Console.WriteLine("Parse Arguments Completely!");

                    System.Console.WriteLine("Initialize Network Service With:");
                    System.Console.WriteLine("Max Session:{0}", nMaxSession);
                    System.Console.WriteLine("Theads Number Of I/O Service:{0}", nTheads);
                    System.Console.WriteLine("Bytes Length Per Buffer:{0}", m_bufferLength);

                    for (int i = 0; i < m_worldGroup.Length; ++i)
                    {
                        m_worldGroup[i] = new WorldGroup();
                    }

                    if (!GameServerGroupInfo.LoadFromFile("GameServerGroup.gsg"))
                        break;

                    if (!PosServerGroupInfo.LoadFromFile("PosServerGroup.psg"))
                        break;

                    NetLib.initialize_service(nTheads, nMaxSession, m_bufferLength);

                    m_messageCache = new MessageCache(m_bufferLength, 100);
                    tempUnit = new MessageCacheUnit(m_bufferLength);
                    if (m_messageCache == null)
                        break;

                    if (NetLib.is_service_init() == false)
                        break;

                    System.Console.WriteLine("Initialize Network Service Completely!");

                    NetLib.start_service();
                    System.Console.WriteLine("Start Network Service!");

                    if (NetLib.start_listen((UInt16)nPort, OnMessage) == false)
                        break;

                    System.Console.WriteLine("Listen On Port:{0}", nPort);
                    return true;
                }

            } while (false);

            System.Console.WriteLine("An Error Occurs!");
            return false;
        }

        /// <summary>
        /// override run() function
        /// </summary>
        protected override void Run()
        {
            ProcessMessage();
        }
        /// <summary>
        /// override close() function
        /// </summary>
        protected override void Close()
        {
            NetLib.close_listen();
            NetLib.terminate_service();
            NetLib.destroy_service();
            base.Close();
        }
        #endregion

        #region private function
        /// <summary>
        /// process message in the loop
        /// </summary>
        private void ProcessMessage()
        {
            if (m_messageCache.size() > 0)
            {
                m_messageCache.read(ref tempUnit);
                if (tempUnit.SessionId != -1)
                {
                    NetMessageProcessor.ProcessMessage(this, tempUnit.SessionId, tempUnit.MsgId, tempUnit.Buffer, tempUnit.MsgBytes);
                    tempUnit.discard();
                }
            }
            else
                System.Threading.Thread.Sleep(1);
        }
        #endregion

        #region network callback
        /// <summary>
        /// store messages
        /// </summary>
        /// <param name="session_id">identifier of clients(such as login server, database server)</param>
        /// <param name="pMsgBuffer"></param>
        /// <param name="msgID"></param>
        /// <param name="msg_bytes"></param>
        public void StoreMessage(Int32 session_id, IntPtr pMsgBuffer, UInt32 msgID, UInt32 msg_bytes)
        {
            m_messageCache.write(session_id, msgID, msg_bytes, pMsgBuffer);
        }

        /// <summary>
        /// notify to client
        /// </summary>
        public void NotifyToClient(int session_id)
        {
            msgGBServer_Notify sn = new msgGBServer_Notify(session_id, global.eServerType.ServerType_GatewayServer);
            gwMessagePort.ProcessMessage(sn);
        }

        /// <summary>
        /// remove user information
        /// </summary>
        /// <param name="session_id"></param>
        public void RemoveUsrInfo(int session_id)
        {
            m_usrInfoCache.RemoveBySessionId(session_id);
        }

        /// <summary>
        /// process incoming message
        /// </summary>
        /// <param name="session_id"></param>
        /// <param name="pMsgBuffer"></param>
        /// <param name="msgID"></param>
        /// <param name="msg_bytes"></param>
        /// <param name="type"></param>
        /// <param name="error_code"></param>
        private static void OnMessage(Int32 session_id, IntPtr pMsgBuffer, UInt32 msgID, UInt32 msg_bytes, Int32 type, Int32 error_code)
        {
            NetMsgProcDispatcher.dipatch<GatewayServer>(GetInstance, type, session_id, pMsgBuffer, msgID, msg_bytes, error_code);
        }

        /// <summary>
        /// find the position server which this game server need
        /// </summary>
        /// <param name="gs_world_id"></param>
        /// <returns></returns>
        private List<int> FindPosServer(int gs_grouId)
        {
            List<int> valid = new List<int>();
            List<int> worldList = GameServerGroupInfo.GetWorldList(gs_grouId);
            if (worldList != null)
            {
                foreach (int world_id in worldList)
                {
                    int group_id = PosServerGroupInfo.FindWorldGroup(world_id);
                    if (group_id != GlobalDef.INVALID_WORLD_GROUP)
                    {
                        if (valid.BinarySearch(group_id) < 0)
                            valid.Add(group_id);
                    }
                    else
                        System.Console.WriteLine("Some Position Server Not Register!");
                }
            }
            else
                System.Console.WriteLine("Can Not Find This Game Server Group!");

            return valid;
        }

        /// <summary>
        /// send server list that has been requested
        /// </summary>
        /// <param name="session_id"></param>
        /// <param name="request"></param>
        public void SendServerList(int session_id, gatewayserver.ServerInfoRequest request)
        {
            List<ServerInfoCache.CacheUnit> server_list = ServerInfo.GetServeInfoList(request.ServerType);
            List<gatewayserver.ServerInfoFeedback.ServerDtail> server_detail = new List<gatewayserver.ServerInfoFeedback.ServerDtail>();
            if (request.ServerType == global.eServerType.ServerType_PosServer)
            {
                List<int> valid_group = FindPosServer(request.WorldGroup);
                foreach (int group_id in valid_group)
                {
                    foreach (ServerInfoCache.CacheUnit server_info in server_list)
                    {
                        if (server_info.WorldGroup == group_id)
                        {
                            List<int> world_id = PosServerGroupInfo.GetWorldList(group_id);
                            gatewayserver.ServerInfoFeedback.ServerDtail detail = new gatewayserver.ServerInfoFeedback.ServerDtail();
                            detail.Info.IpAddr = server_info.IpAddr;
                            detail.Info.Port = server_info.Port;
                            detail.Info.ServerType = server_info.ServerType;
                            detail.Info.WorldGroup = server_info.WorldGroup;
                            detail.WorldId.AddRange(world_id);
                            server_detail.Add(detail);
                            break;
                        }
                    }
                }
            }
            else
            {
                foreach (ServerInfoCache.CacheUnit server_info in server_list)
                {
                    gatewayserver.ServerInfoFeedback.ServerDtail detail = new gatewayserver.ServerInfoFeedback.ServerDtail();
                    detail.Info.IpAddr = server_info.IpAddr;
                    detail.Info.Port = server_info.Port;
                    detail.Info.ServerType = server_info.ServerType;
                    detail.Info.WorldGroup = server_info.WorldGroup;
                    server_detail.Add(detail);
                    break;
                }
            }

            msgGWServerInfo_Feedback msg = new msgGWServerInfo_Feedback(session_id, request.ServerType, server_detail);
            gwMessagePort.ProcessMessage(msg);
        }
        #endregion

        #region property
        //server information cache
        private ServerInfoCache m_infoCache = new ServerInfoCache();
        /// <summary>
        /// server information cache
        /// </summary>
        public ServerInfoCache ServerInfo
        {
            get
            {
                return m_infoCache;
            }
            private set
            {
                m_infoCache = value;
            }
        }
        //user information
        UsrInfoCache m_usrInfoCache = new UsrInfoCache(10000);
        /// <summary>
        /// user information
        /// </summary>
        public UsrInfoCache UsrInfo
        {
            get
            {
                return m_usrInfoCache;
            }
            private set
            {
                m_usrInfoCache = value;
            }
        }
        //auth server ip address
        private string m_authIP;
        /// <summary>
        /// auth server ip address
        /// </summary>
        public string AuthServerIP
        {
            get
            {
                return m_authIP;
            }
            private set
            {
                m_authIP = value;
            }
        }
        //auth server port
        private int m_authPort;
        /// <summary>
        /// auth server port
        /// </summary>
        public int AuthServerPort
        {
            get
            {
                return m_authPort;
            }
            private set
            {
                m_authPort = value;
            }
        }
        //auth server session handle
        private int m_authHandle = NetLib.INVALID_SESSION;
        /// <summary>
        /// auth server session handle
        /// </summary>
        public int AuthHandle
        {
            get
            {
                lock (m_lockObj)
                {
                    return m_authHandle;
                }
            }
            set
            {
                lock (m_lockObj)
                {
                    m_authHandle = value;
                }
            }
        }

        //information about world gorup
        private WorldGroup[] m_worldGroup = new WorldGroup[Enum.GetNames(typeof(global.WorldGroupInfo.ServerType)).Length];
        /// <summary>
        /// information about game server world group
        /// </summary>
        public WorldGroup GameServerGroupInfo
        {
            get
            {
                return m_worldGroup[(int)global.WorldGroupInfo.ServerType.GameServer];
            }
            private set
            {
                m_worldGroup[(int)global.WorldGroupInfo.ServerType.GameServer] = value;
            }
        }

        /// <summary>
        /// information about pos server world group
        /// </summary>
        public WorldGroup PosServerGroupInfo
        {
            get
            {
                return m_worldGroup[(int)global.WorldGroupInfo.ServerType.PosServer];
            }
            private set
            {
                m_worldGroup[(int)global.WorldGroupInfo.ServerType.PosServer] = value;
            }
        }
        #endregion
    }
}
